#include"Connection.h"

Connection::Connection(EventLoop *loop, Socket *clientsock):loop_(loop),clientsock_(clientsock), disconnect_(false)
{
     // 连接上来的客户端采用边缘事件的读事件
    clientchannel_ = new Channel(loop_, clientsock_->fd());
    clientchannel_->setreadcallback(std::bind(&Connection::onmessage, this));
    clientchannel_->setclosecallback(std::bind(&Connection::closecallback, this));
    clientchannel_->seterrorcallback(std::bind(&Connection::errorcallback, this));
    clientchannel_->setwritecallback(std::bind(&Connection::writecallback, this));
    clientchannel_->setet();
    clientchannel_->enablereading();
}

Connection::~Connection()
{
    delete clientsock_;
    delete clientchannel_;
}



int Connection::fd() const
{
    return clientsock_->fd();
}

std::string Connection::ip()
{
    return clientsock_->ip();
}

uint16_t Connection::port()
{
    return clientsock_->port();
}

void Connection::onmessage()
{
    char buffer[1024]; // 存放从客户端读取的数据。
    while(true)
    {
        memset(buffer, 0, sizeof(buffer));
        ssize_t nread = read(fd(), buffer, sizeof(buffer));
        // 成功读取到了数据
        if(nread > 0)
        {
            // 将读取到的数据追加到接受缓冲区中--只是为了解决黏包和分包
            inputbuffer_.append(buffer, nread);
        }
        // 读取数据时信号中断，继续重试读取
        else if(nread == -1 && errno == EINTR)
        {
            continue;
        }
        // 数据被全部读取完毕
        else if(nread == -1 && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
        {
            // 解决黏包和分包的具体处理 [目前做法是 报文头部+报文内容； 报文头部：4个字节大小的数字，用来说明报文内容的长度],但也限制了客户端也必须要这样发送了
            while(true)
            {
                /////////////////////////////////////////////////////////////////////////////////////////////
                // 可以把下面的代码封装在Buffer类中，可以使用三种封装；1.固定长度；2.报文头部（当前方法）；3.指定分隔符

                int len = 0;
                // 获取报文头部
                memcpy(&len, inputbuffer_.data(), 4);
                // 如果接受缓冲区的字节大小小于 报文头部说明的大小，代表报文内容不完整，等待下次接受数据
                if(inputbuffer_.size() < len + 4)
                {
                    break;
                }
                // 获取报文内容
                std::string message(inputbuffer_.data() + 4, len);
                // 从接受缓冲区中删除这次报文的数据
                inputbuffer_.erase(0, len + 4);

                /////////////////////////////////////////////////////////////////////////////////////////////
                
                // 回调处理消息 TcpServer::Onmessage
                onmessagecallback_(shared_from_this(), message);
            }
            break;
        }
        // 客户端断开连接
        else if(nread == 0)
        {
            closecallback();
            break;
        }
    }
}


void Connection::closecallback()
{
    disconnect_ = true;
    clientchannel_->remove();
    closecallback_(shared_from_this());
}

void Connection::errorcallback()
{
    disconnect_ = true;
    clientchannel_->remove();
    errorcallback_(shared_from_this());
}


void Connection::writecallback()
{
    // 尝试将发送缓冲区中的数据全部发生出去【再次说明一下，发送缓冲区就是一个string对象而已，即outputbuffer_，只是用来存储要发生的数据罢了】
    int writen = ::send(fd(), outputbuffer_.data(), outputbuffer_.size(), 0);
    if(writen > 0)
    {
        // 从发送缓冲区中删除已经发送出去的数据
        outputbuffer_.erase(0, writen);
    }
    // 如果发送缓冲区中没有数据了，就取消监听写事件，避免无效的 busy loop
    if(outputbuffer_.size() == 0)
    {
        clientchannel_->disablewriting();
        // 通知发送缓冲区数据发送完成了，调用TcpServer::sendcomplete()
        sendcompletecallback_(shared_from_this());
    }
}


void Connection::setclosecallback(std::function<void(std::shared_ptr<Connection>)> fn)
{
    closecallback_ = fn;
}
void Connection::seterrorcallback(std::function<void(std::shared_ptr<Connection>)> fn)
{
    errorcallback_ = fn;
}

void Connection::setonmessagecallback(std::function<void(std::shared_ptr<Connection>, std::string&)> fn)
{
    onmessagecallback_ = fn;
}

void Connection::setsendcompletecallback(std::function<void(std::shared_ptr<Connection>)> fn)
{
    sendcompletecallback_ = fn;
}

void Connection::send(const char *data, size_t size)
{
    // 如果连接已经断开了，那么不需要发送数据了
    if(disconnect_ == true)
    {
        return ;
    }
    // 把需要发送的数据保存到Conn的输出缓冲区中
    outputbuffer_.appendwithhead(data, size);
    // 注册写事件
    clientchannel_->enablewriting();
}
